#ifndef __M_EXCHANGE_H__
#define __M_EXCHANGE_H__
#include "../mqcommon/mq_logger.hpp"
#include "../mqcommon/mq_helper.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include <google/protobuf/map.h>
#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <mutex>

// 定义交换机类
namespace csjmq
{
    struct Exchange
    {
        using ptr = std::shared_ptr<Exchange>;
        // 1.交换机名称
        std::string name;
        // 2.交换机类型
        ExchangeType type;
        // 3.是否持久化标志
        bool durable;
        // 4.是否自动删除
        bool auto_delete;
        // 5.其他参数
        google::protobuf::Map<std::string, std::string> args;
        Exchange() {}
        Exchange(const std::string &ename, ExchangeType etype,
                 bool edurable, bool eauto_delete,
                 const google::protobuf::Map<std::string, std::string> &eargs)
            : name(ename), type(etype), durable(edurable),
              auto_delete(eauto_delete),
              args(eargs)
        {
        }

        // args存储键值对，在存储数据库的时候，会组织一个格式字符串进行存储 key=val&key=val...
        // 内部解析str_args，然后存储到成员当中
        void setArgs(const std::string &str_args)
        {
            // key=val&key=val
            std::vector<std::string> sub_args;
            StrHelper::split(str_args, "&", sub_args);
            for (const auto &sub : sub_args)
            {
                auto pos = sub.find("=");
                std::string key = sub.substr(0, pos);
                std::string val = sub.substr(pos + 1);
                args[key] = val;
            }
        }

        // 将成员中的数据拿出来进行序列化，以便以数据库存储key=vla&key=val...
        std::string getArgs()
        {
            // key=val&key=val&
            std::string result;
            for (const auto &str : args)
            {
                result += (str.first + "=" + str.second + "&");
            }
            return result;
        }
    };

    // 定义交换机数据持久化管理类 -- 数据存储在sqlite库中
    class ExchangeMapper
    {
    public:
        ExchangeMapper(const std::string &dbfile) : _sql_helper(dbfile)
        {
            std::string path = FileHelper::parentDirectory(dbfile);
            FileHelper::createDirectory(path); // 现将父级路径创建出来
            assert(_sql_helper.open());        // 然后再创建该文件
            createTable();
        }

        void createTable()
        {
#define CREATE_TABLE "create table if not exists exchange_table (\
        name varchar(32) primary key,\
        type int,\
        durable int,\
        auto_delete int,\
        args varchar(128));"
            int ret = _sql_helper.exec(CREATE_TABLE, nullptr, nullptr);
            if (!ret)
            {
                ELOG("交换机数据库创建失败");
                abort();
            }
        }

        void removeTable()
        {
#define DELETE_TABLE "drop table if exists exchange_table"
            int ret = _sql_helper.exec(DELETE_TABLE, nullptr, nullptr);
            if (!ret)
            {
                ELOG("删除交换机数据库失败");
                abort();
            }
        }

        // 在表中新增交换机内容
        bool insert(Exchange::ptr &exp)
        {
            std::stringstream ss;
            ss << "insert into exchange_table values(";
            ss << "'" << exp->name << "',";
            ss << exp->type << ",";
            ss << exp->durable << ",";
            ss << exp->auto_delete << ",";
            ss << "'" << exp->getArgs() << "');";
            int ret = _sql_helper.exec(ss.str(), nullptr, nullptr);
            if (!ret)
            {
                ELOG("插入数据失败");
                return false;
            }
            return true;
        }

        // 依靠交换机的名字，在表中删除该交换机
        void remove(const std::string &name)
        {
            std::stringstream ss;
            ss << "delete from exchange_table where name = ";
            ss << "'" << name << "';";
            _sql_helper.exec(ss.str(), nullptr, nullptr);
        }

        using ExchangeMap = std::unordered_map<std::string, Exchange::ptr>;
        ExchangeMap recovery()
        {
            ExchangeMap result;
            std::string select_sql = "select name, type, durable, auto_delete, args from exchange_table;";
            _sql_helper.exec(select_sql, selectCallback, &result);
            return result;
        }

    private:
        static int selectCallback(void *arg, int col_count, char **row, char **fields)
        {
            ExchangeMap *result = (ExchangeMap *)arg;
            auto exp = std::make_shared<Exchange>();
            exp->name = row[0];
            exp->type = (ExchangeType)std::stoi(row[1]);
            exp->durable = (bool)std::stoi(row[2]);
            exp->auto_delete = (bool)std::stoi(row[3]);
            if (row[4])
                exp->setArgs(row[4]);
            result->insert(std::make_pair(exp->name, exp));
            return 0;
        }

    private:
        SqliteHelper _sql_helper;
    };

    // 定义交换机数据内存管理类
    class ExchangeManager
    {
    public:
        using ptr = std::shared_ptr<ExchangeManager>;
        ExchangeManager(const std::string &dbfile)
            : _mapper(dbfile)
        {
            _exchanges = _mapper.recovery(); // 将数据库中的数据加载到内存中
        }

        // 声明/创建交换机
        bool declareExchange(const std::string &name, ExchangeType type,
                             bool durable, bool auto_delete,
                             const google::protobuf::Map<std::string, std::string> &args)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            // 判断交换机是否存在
            auto it = _exchanges.find(name);
            if (it != _exchanges.end())
                return true;
            auto exp = std::make_shared<Exchange>(name, type, durable, auto_delete, args);
            if (durable == true) // 如果是持久化存储，就放入到数据库中
            {
                bool ret = _mapper.insert(exp);
                if (!ret)
                    return false;
            }
            _exchanges.insert(std::make_pair(name, exp));
            return true;
        }

        // 删除交换机
        void deleteExchange(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            // 判断交换机是否存在
            auto it = _exchanges.find(name);
            if (it == _exchanges.end())
                return;
            if (it->second->durable == true)
                _mapper.remove(name);
            _exchanges.erase(name);
        }

        // 获取指定交换机对象
        Exchange::ptr selectExchange(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            if (it == _exchanges.end())
            {
                return Exchange::ptr();
            }
            return it->second;
        }

        // 3.判断交换机是否存在
        bool exists(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            // 判断交换机是否存在
            auto it = _exchanges.find(name);
            if (it != _exchanges.end())
                return true;
            return false;
        }

        // 获取交换机的数量
        size_t size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _exchanges.size();
        }

        // 4.清理交换机
        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.removeTable();
            _exchanges.clear();
        }

    private:
        std::mutex _mutex;
        ExchangeMapper _mapper;
        std::unordered_map<std::string, Exchange::ptr> _exchanges;
    };
}
#endif
